{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "KnClcNA1HEFe"
   },
   "source": [
    "## Introduction\n",
    "\n",
    "The goal of the BeeAI project is to make AI agents interoperable, regardless of their underlying implementation. The project consists of two key components:\n",
    "- **BeeAI Platform**: The platform to easily discover, run, and compose AI agents from any framework.\n",
    "- **BeeAI Framework**: A production-grade framework for building AI agents in either Python or TypeScript.\n",
    "\n",
    "Detailed information on BeeAI can be found [here](https://beeai.dev/).\n",
    "\n",
    "### What's in this notebook?\n",
    "\n",
    "This notebook demonstrates the BeeAI Requirement agent, a declarative AI agent that combines language models, tools, and execution requirements to create predictable, controlled behavior across different LLMs.\n",
    "\n",
    "Why Use Requirement Agent?\n",
    "- Building agents that work reliably across multiple LLMs is difficult. Most agents are tightly tuned to specific models, with rigid prompts that cause models to misinterpret instructions, skip tools, or hallucinate facts.\n",
    "- RequirementAgent provides a declarative framework for designing agents that strikes a balance between flexibility and control. It allows for agent behavior that is both predictable and adaptable, without the complexity and limitations of more rigid systems.\n",
    "- The core concept behind the Requirement Agent is that everything is treated as a tool—including data retrieval, web search, reasoning, and generating final answers. This modular structure ensures valid responses with structured outputs and helps eliminate parsing errors.\n",
    "\n",
    "You can run this notebook on [**Google Colab**](https://colab.research.google.com/). The notebook uses **Ollama** to provide access to a variety of foundation models for remote execution. The notebook will run faster on Colab if you use the free *T4 GPU* option by selecting *Runtime / Change runtime type* in the Colab system menu."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "E_3B1fBNfY2_"
   },
   "source": [
    "Run the Next Cell to wrap Notebook output."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "MCK7aDzYfZoA"
   },
   "outputs": [],
   "source": [
    "from IPython.display import HTML, display\n",
    "\n",
    "\n",
    "def set_css():\n",
    "    display(HTML(\"\\n<style>\\n pre{\\n white-space: pre-wrap;\\n}\\n</style>\\n\"))\n",
    "\n",
    "\n",
    "get_ipython().events.register(\"pre_run_cell\", set_css)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GzaYKuOJ_ata"
   },
   "source": [
    "### Install Libraries\n",
    "We start by installing the required dependencies, installing Ollama and pulling the granite model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "AhEZP5JMHEFo"
   },
   "outputs": [],
   "source": [
    "%pip install -q langchain_community wikipedia requests==2.32.4 beeai-framework\n",
    "\n",
    "!curl -fsSL https://ollama.com/install.sh | sh > /dev/null\n",
    "!nohup ollama serve >/dev/null 2>&1 &\n",
    "!ollama pull granite3.3:8b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "4gNKn0Wy_lDD"
   },
   "source": [
    "### Import Libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "fYYNyeIyHEFq"
   },
   "outputs": [],
   "source": [
    "# Imports\n",
    "from langchain_community.tools import WikipediaQueryRun\n",
    "from langchain_community.utilities import WikipediaAPIWrapper\n",
    "from pydantic import BaseModel, Field\n",
    "\n",
    "from beeai_framework.agents.requirement import RequirementAgent\n",
    "from beeai_framework.backend.chat import ChatModel\n",
    "from beeai_framework.context import RunContext\n",
    "from beeai_framework.emitter.emitter import Emitter\n",
    "from beeai_framework.memory.unconstrained_memory import UnconstrainedMemory\n",
    "from beeai_framework.tools import StringToolOutput, Tool, tool\n",
    "from beeai_framework.tools.types import ToolRunOptions\n",
    "from beeai_framework.tools.weather.openmeteo import OpenMeteoTool\n",
    "\n",
    "print(\"Imports and credentials completed\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "7Gdul_gpHEFs"
   },
   "source": [
    "## Basic Requirement agent\n",
    "\n",
    "To configure a Requirement agent, you need to define a ChatModel and construct an Agent.\n",
    "\n",
    "In this example, we won't provide any external tools to the agent. It will rely solely on its own memory to provide answers. This is a basic setup where the agent tries to reason and act based on the context it has built internally.\n",
    "\n",
    "Try modifying the input text in the call to agent.run() to experiment with obtaining different answers. This will help you see how the agent's reasoning and response vary with different prompts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "zV9WMuOdHEFt"
   },
   "outputs": [],
   "source": [
    "# Construct ChatModel\n",
    "chat_model = ChatModel.from_name(\"ollama:granite3.3\")\n",
    "\n",
    "# Construct Agent instance with the chat model\n",
    "agent = RequirementAgent(llm=chat_model, tools=[], memory=UnconstrainedMemory())\n",
    "\n",
    "# Run the agent\n",
    "message = \"What chemical elements make up a water molecule?\"\n",
    "print(\"Question: \" + message + \"\\n\")\n",
    "response = await agent.run(message)\n",
    "print(response.last_message.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "-E6ubJbyHEFu"
   },
   "source": [
    "## Provide tools to the agent\n",
    "\n",
    "To go beyond just chatting with an LLM, you can provide tools to the agent. This enables the agent to perform specific tasks and interact with external systems, enhancing its functionality. There are different ways to add tools to the agent:\n",
    "\n",
    "- Built-in tools from the framework: BeeAI provides several built-in tools that you can easily integrate with your agent.\n",
    "- Importing tools from other libraries: You can bring in external tools or APIs to extend your agent's capabilities.\n",
    "- Custom tooling: You can also write your own custom tools tailored to your specific use case.\n",
    "\n",
    "By equipping the agent with these tools, you allow it to perform more complex actions, such as querying databases, interacting with APIs, or manipulating data.\n",
    "\n",
    "### Built-in tools\n",
    "\n",
    "BeeAI comes with several built-in tools that are part of the library, which can be easily imported and added to your agent.\n",
    "\n",
    "In this example, we provide the agent with a weather forecast lookup tool called OpenMeteoTool. With this tool, the agent can retrieve real-time weather data, enabling it to answer weather-related queries with more accuracy.\n",
    "\n",
    "Follow the agent's thoughts and actions to understand how it approaches and solves the problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "T99FLsoAHEFv"
   },
   "outputs": [],
   "source": [
    "# create an agent using the default LLM and add the OpenMeteoTool that is capable of fetching weather-based information\n",
    "agent = RequirementAgent(llm=chat_model, tools=[OpenMeteoTool()], memory=UnconstrainedMemory())\n",
    "\n",
    "# Run the agent\n",
    "message = \"What's the current weather in London?\"\n",
    "print(\"Question: \" + message + \"\\n\")\n",
    "\n",
    "response = await agent.run(message)\n",
    "print(response.last_message.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ZkjHP4DdHEFw"
   },
   "source": [
    "## Imported tools\n",
    "\n",
    "Tools can also be imported from other libraries to extend the functionality of your agent. For example, you can integrate tools from libraries like LangChain to give your agent access to even more capabilities.\n",
    "\n",
    "Here’s an example showing how to integrate the Wikipedia tool from LangChain, written in long form (without using the @tool decorator):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "AM7hoavlHEFx"
   },
   "outputs": [],
   "source": [
    "class LangChainWikipediaToolInput(BaseModel):\n",
    "    query: str = Field(description=\"The topic or question to search for on Wikipedia.\")\n",
    "\n",
    "\n",
    "class LangChainWikipediaTool(Tool[LangChainWikipediaToolInput, ToolRunOptions, StringToolOutput]):\n",
    "    \"\"\"Adapter class to integrate LangChain's Wikipedia tool with our framework\"\"\"\n",
    "\n",
    "    name = \"Wikipedia\"\n",
    "    description = \"Search factual and historical information from Wikipedia about given topics.\"\n",
    "    input_schema = LangChainWikipediaToolInput\n",
    "\n",
    "    def __init__(self) -> None:\n",
    "        super().__init__()\n",
    "        self._wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())\n",
    "\n",
    "    def _create_emitter(self) -> Emitter:\n",
    "        return Emitter.root().child(\n",
    "            namespace=[\"tool\", \"search\", \"langchain_wikipedia\"],\n",
    "            creator=self,\n",
    "        )\n",
    "\n",
    "    async def _run(\n",
    "        self, input: LangChainWikipediaToolInput, options: ToolRunOptions | None, context: RunContext\n",
    "    ) -> StringToolOutput:\n",
    "        query = input.query\n",
    "        try:\n",
    "            result = self._wikipedia.run(query)\n",
    "            return StringToolOutput(result=result)\n",
    "        except Exception as e:\n",
    "            print(f\"Wikipedia search error: {e!s}\")\n",
    "            return f\"Error searching Wikipedia: {e!s}\"\n",
    "\n",
    "\n",
    "agent = RequirementAgent(llm=chat_model, tools=[LangChainWikipediaTool()], memory=UnconstrainedMemory())\n",
    "message = \"Who is the current president of the European Commission?\"\n",
    "print(\"Question: \" + message + \"\\n\")\n",
    "\n",
    "response = await agent.run(message)\n",
    "print(response.last_message.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "nZvQk6NeHEFy"
   },
   "source": [
    "The previous example can be re-written in a shorter form by adding the @tool decorator, which simplifies the tool definition. Here's how you can do it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "aVFfyV-1HEFy"
   },
   "outputs": [],
   "source": [
    "# defining a tool using the `tool` decorator\n",
    "# Note: the pydoc is important as it serves as the tool description to the agent\n",
    "@tool\n",
    "def langchain_wikipedia_tool(query: str) -> str:\n",
    "    \"\"\"\n",
    "    Search factual and historical information, including biography, history, politics, geography, society, culture,\n",
    "    science, technology, people, animal species, mathematics, and other subjects.\n",
    "\n",
    "    Args:\n",
    "        query: The topic or question to search for on Wikipedia.\n",
    "\n",
    "    Returns:\n",
    "        The information found via searching Wikipedia.\n",
    "    \"\"\"\n",
    "    wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())\n",
    "    return wikipedia.run(query)\n",
    "\n",
    "\n",
    "# using the tool in an agent\n",
    "agent = RequirementAgent(llm=chat_model, tools=[langchain_wikipedia_tool], memory=UnconstrainedMemory())\n",
    "message = \"What is the longest living vertebrate?\"\n",
    "print(\"Question: \" + message + \"\\n\")\n",
    "\n",
    "response = await agent.run(message)\n",
    "print(response.last_message.text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "xiFoAsQpHEFz"
   },
   "source": [
    "## Learn More\n",
    "\n",
    "Detailed information on BeeAI can be found [here](https://beeai.dev/).\n",
    "\n",
    "In this notebook, you learned how to build BeeAI Requirement agents in the BeeAI Framework."
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "gpuType": "T4",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "name": "python3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
